package com.bj58.ecdata.calc.dbservice.dao;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.PermissionDeniedDataAccessException;

import redis.clients.jedis.Tuple;

import com.bj58.ecdata.calc.dbservice.dao.annotation.AccZSetDimension;
import com.bj58.ecdata.calc.dbservice.dao.annotation.DailyMultiDimensionAccValueMap;
import com.bj58.ecdata.calc.dbservice.dao.annotation.DailyMultiDimensionZSet;
import com.bj58.ecdata.calc.dbservice.dao.annotation.DailySumCacheValue;
import com.bj58.ecdata.calc.dbservice.dao.annotation.HourlyMultiDimensionAccValueMap;
import com.bj58.ecdata.calc.dbservice.dao.annotation.MapDimension;
import com.bj58.ecdata.calc.dbservice.dao.annotation.MinuteMultiDimensionAccValueMap;
import com.bj58.ecdata.calc.dbservice.dao.annotation.MultiDimensionAccValueMap;
import com.bj58.ecdata.calc.dbservice.dao.consts.DaoConst;
import com.bj58.ecdata.calc.dbservice.dao.model.DimensionInfo;
import com.bj58.ecdata.calc.dbservice.dao.model.FieldInfo;
import com.bj58.ecdata.redis.JedisX;
import com.bj58.ecdata.thread.ThreadUtils;
import com.bj58.ecdata.util.Algorithm;
import com.bj58.ecdata.util.DateUtils;
import com.bj58.ecdata.util.NumberUtils;
import com.bj58.ecdata.util.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * redis相关操作
 * @author lichao
 */
public abstract class CacheBaseDao<T> extends JdbcBaseDao<T> {

	private Logger log = LoggerFactory.getLogger(CacheBaseDao.class);

	@Autowired(required = false)
	@Qualifier("cachePool")
	protected JedisX cache;

	/**
	 * 日累加
	 * tablename_sum_dimension_:today
	 */
	private Map<String, DimensionInfo> dailySumDimensionInfos = Maps.newHashMap();

	/**
	 * 排序集，用于计算TOP
	 * tablename_acczset_dimension_:today
	 */
	private Map<String, DimensionInfo> accZSetDimensionInfos = Maps.newHashMap();

	/**
	 * 最初用于分直销、渠道 统计各个城市的pv,现在没啥用
	 * tablename_map_time_:time_dimension_:value
	 */
	private Map<String, DimensionInfo> mapDimensionInfos = Maps.newHashMap();

	/**
	 * <pre>
	 * 多维度，不做累加汇总
	 * tablename_mdmap_time_:time
	 * dimension1_:dimension1_dimension2_:dimension2_dimension
	 * </pre>
	 */
	private Map<String, DimensionInfo> multiDimensionMapInfos = Maps.newHashMap();

	/**
	 * <pre>
	 * 多维度，小时汇总
	 * tablename_mdmap_hour_:hour
	 * dimension1_:dimension1_dimension2_:dimension2_dimension
	 * </pre>
	 */
	private Map<String, DimensionInfo> hourlyMultiDimensionMapInfos = Maps.newHashMap();

	/**
	 * <pre>
	 * 多维度，分钟汇总
	 * tablename_mdmap_minute_:minute
	 * dimension1_:dimension1_dimension2_:dimension2_dimension
	 * </pre>
	 */
	private Map<String, DimensionInfo> minuteMultiDimensionMapInfos = Maps.newHashMap();

	/**
	 * <pre>
	 * 多维度，日汇总
	 * tablename_mdmap_daily_:today
	 * dimension1_:dimension1_dimension2_:dimension2_dimension
	 * </pre>
	 */
	private Map<String, DimensionInfo> dailyMultiDimensionMapInfos = Maps.newHashMap();

	/**
	 * <pre>
	 * 多维度，日排序集，最初用于算搜索词，目前没有用到
	 * tablename_mdzset_dimension1_:dimension1_dimension2_:dimension2_dimension_:today
	 * </pre>
	 */
	private Map<String, DimensionInfo> dailyMultiDimensionZSetInfos = Maps.newHashMap();

	private ExecutorService threadPool = ThreadUtils.newCachedThreadPool(super.tableName);

	private String latestCacheKey; // 最新的主维度值
	/**
	 * tablename_detail_time_:time
	 */
	private static String detailCacheKeyPattern;

	@Override
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		String primaryDimensionName = super.primaryDimension.getName();
		latestCacheKey = super.tableName + "_latest";

		// tablename_detail_time_:time
		detailCacheKeyPattern = StringUtils.joinWithUnderline(super.tableName, "detail", primaryDimensionName, "%s");

		for (FieldInfo fieldInfo : super.fieldInfos.values()) {
			Field field = fieldInfo.getField();
			
			DailySumCacheValue dailySumCacheValue = field.getAnnotation(DailySumCacheValue.class);
			if (null != dailySumCacheValue) {
				DimensionInfo dimensionInfo = new DimensionInfo();
				dimensionInfo.setDimension(fieldInfo);
				// tablename_sum_dimension_:today
				dimensionInfo.setCacheKeyPattern(StringUtils.joinWithUnderline(super.tableName, "sum", field.getName(), "%d"));
				dailySumDimensionInfos.put(fieldInfo.getName(), dimensionInfo);
			}

			AccZSetDimension accZSetDimension = field.getAnnotation(AccZSetDimension.class);
			if (accZSetDimension != null) {
				DimensionInfo dimensionInfo = new DimensionInfo();
				dimensionInfo.setDimension(fieldInfo);
				dimensionInfo.setCacheValue(super.fieldInfos.get(accZSetDimension.cacheValue()));
				// tablename_acczset_dimension_:today
				dimensionInfo.setCacheKeyPattern(StringUtils.joinWithUnderline(super.tableName, "acczset", field.getName(), "%d"));
				accZSetDimensionInfos.put(fieldInfo.getName(), dimensionInfo);
			}

			DailyMultiDimensionZSet dailyMultiDimensionZSet = field.getAnnotation(DailyMultiDimensionZSet.class);
			if (dailyMultiDimensionZSet != null) {

				List<FieldInfo> multiDimension = Lists.newArrayList();
				for (String fieldName : dailyMultiDimensionZSet.dimensions()) {
					multiDimension.add(super.fieldInfos.get(fieldName));
				}

				// dimension1_:dimension1_dimension2_:dimension2_dimension
				String multiDimensionKey = joinDimensions(multiDimension).append(fieldInfo.getName()).toString();
				// tablename_mdzset_dimension1_:dimension1_dimension2_:dimension2_dimension_:today
				String cacheKeyPattern = StringUtils.joinWithUnderline(super.tableName, "mdzset", multiDimensionKey, "%s");

				DimensionInfo dimensionInfo = new DimensionInfo();
				dimensionInfo.setDimension(fieldInfo);
				dimensionInfo.setCacheValue(super.fieldInfos.get(dailyMultiDimensionZSet.cacheValue()));
				dimensionInfo.setCombination(Algorithm.allCombination(multiDimension));
				dimensionInfo.setCacheKeyPattern(cacheKeyPattern);
				dimensionInfo.setMultiDimension(multiDimension);
				dailyMultiDimensionZSetInfos.put(fieldInfo.getName(), dimensionInfo);

			}

			MapDimension mapDimension = field.getAnnotation(MapDimension.class);
			if (mapDimension != null) {
				DimensionInfo dimensionInfo = new DimensionInfo();
				dimensionInfo.setDimension(fieldInfo);
				dimensionInfo.setCacheKey(super.fieldInfos.get(mapDimension.cacheKey()));
				dimensionInfo.setCacheValue(super.fieldInfos.get(mapDimension.cacheValue()));
				// tablename_map_time_:time_dimension_:value
				dimensionInfo.setCacheKeyPattern(StringUtils.joinWithUnderline(super.tableName, "map", primaryDimensionName, "%s",
						fieldInfo.getName(), "%s"));
				mapDimensionInfos.put(fieldInfo.getName(), dimensionInfo);
			}

			MultiDimensionAccValueMap multiDimensionAccValueMap = field.getAnnotation(MultiDimensionAccValueMap.class);
			if (multiDimensionAccValueMap != null) {

				List<FieldInfo> multiDimension = Lists.newArrayList();
				for (String fieldName : multiDimensionAccValueMap.dimensions()) {
					multiDimension.add(super.fieldInfos.get(fieldName));
				}

				// tablename_mdmap_time_:time
				String primaryDimensionKey = StringUtils.joinWithUnderline(super.tableName, "mdmap", primaryDimensionName, "%s");
				// dimension1_:dimension1_dimension2_:dimension2_dimension
				String multiDimensionKey = joinDimensions(multiDimension).append(fieldInfo.getName()).toString();

				DimensionInfo dimensionInfo = new DimensionInfo();
				dimensionInfo.setDimension(fieldInfo);
				dimensionInfo.setCombination(Algorithm.allCombination(multiDimension));
				dimensionInfo.setPrimaryDimensionKey(primaryDimensionKey);
				dimensionInfo.setMultiDimensionKey(multiDimensionKey);
				multiDimensionMapInfos.put(fieldInfo.getName(), dimensionInfo);
			}

			MinuteMultiDimensionAccValueMap minuteMultiDimensionAccValueMap = field.getAnnotation(MinuteMultiDimensionAccValueMap.class);
			if (minuteMultiDimensionAccValueMap != null) {

				List<FieldInfo> multiDimension = Lists.newArrayList();
				for (String fieldName : minuteMultiDimensionAccValueMap.dimensions()) {
					multiDimension.add(super.fieldInfos.get(fieldName));
				}

				// tablename_mdmap_minute_:minute
				String primaryDimensionKey = StringUtils.joinWithUnderline(super.tableName, "mdmap", "minute", "%s");
				// dimension1_:dimension1_dimension2_:dimension2_dimension
				String multiDimensionKey = joinDimensions(multiDimension).append(fieldInfo.getName()).toString();

				DimensionInfo dimensionInfo = new DimensionInfo();
				dimensionInfo.setDimension(fieldInfo);
				dimensionInfo.setCombination(Algorithm.allCombination(multiDimension));
				dimensionInfo.setPrimaryDimensionKey(primaryDimensionKey);
				dimensionInfo.setMultiDimensionKey(multiDimensionKey);
				minuteMultiDimensionMapInfos.put(fieldInfo.getName(), dimensionInfo);
			}

			HourlyMultiDimensionAccValueMap hourlyMultiDimensionAccValueMap = field.getAnnotation(HourlyMultiDimensionAccValueMap.class);
			if (hourlyMultiDimensionAccValueMap != null) {

				List<FieldInfo> multiDimension = Lists.newArrayList();
				for (String fieldName : hourlyMultiDimensionAccValueMap.dimensions()) {
					multiDimension.add(super.fieldInfos.get(fieldName));
				}

				// tablename_mdmap_hour_:hour
				String primaryDimensionKey = StringUtils.joinWithUnderline(super.tableName, "mdmap", "hour", "%s");
				// dimension1_:dimension1_dimension2_:dimension2_dimension
				String multiDimensionKey = joinDimensions(multiDimension).append(fieldInfo.getName()).toString();

				DimensionInfo dimensionInfo = new DimensionInfo();
				dimensionInfo.setDimension(fieldInfo);
				dimensionInfo.setCombination(Algorithm.allCombination(multiDimension));
				dimensionInfo.setPrimaryDimensionKey(primaryDimensionKey);
				dimensionInfo.setMultiDimensionKey(multiDimensionKey);
				hourlyMultiDimensionMapInfos.put(fieldInfo.getName(), dimensionInfo);
			}

			DailyMultiDimensionAccValueMap dailyMultiDimensionAccValueMap = field.getAnnotation(DailyMultiDimensionAccValueMap.class);
			if (dailyMultiDimensionAccValueMap != null) {

				List<FieldInfo> multiDimension = Lists.newArrayList();
				for (String fieldName : dailyMultiDimensionAccValueMap.dimensions()) {
					multiDimension.add(super.fieldInfos.get(fieldName));
				}

				// tablename_mdmap_daily_:today
				String primaryDimensionKey = StringUtils.joinWithUnderline(super.tableName, "mdmap", "daily", "%s");
				// dimension1_:dimension1_dimension2_:dimension2_dimension
				String multiDimensionKey = joinDimensions(multiDimension).append(fieldInfo.getName()).toString();

				DimensionInfo dimensionInfo = new DimensionInfo();
				dimensionInfo.setDimension(fieldInfo);
				dimensionInfo.setCombination(Algorithm.allCombination(multiDimension));
				dimensionInfo.setPrimaryDimensionKey(primaryDimensionKey);
				dimensionInfo.setMultiDimensionKey(multiDimensionKey);
				dailyMultiDimensionMapInfos.put(fieldInfo.getName(), dimensionInfo);
			}

		}

	}

	/**
	 * 将多个维度按"dimension_:dimension_"连接
	 */
	private StringBuilder joinDimensions(List<FieldInfo> dimensions) {
		StringBuilder sb = new StringBuilder();
		for (FieldInfo dimension : dimensions) {
			sb.append(dimension.getName()).append("_:").append(dimension.getName()).append("_");
		}
		return sb;
	}

	/**
	 * 向redis插入一条记录
	 * @throws DataAccessException
	 */
	protected T cache_insert(final T model) throws DataAccessException {
		try {
			final long today = DateUtils.getTruncatedToday();
			final String primaryDimensionValue = super.primaryDimension.getReadMethod().invoke(model).toString();
			final CountDownLatch cdl = new CountDownLatch(8);
			// 主维度detail 需求变动不保存
			// String detailCacheKey = String.format(detailCacheKeyPattern,
			// primaryDimensionValue);
			// this.cache.saddObject(detailCacheKey, model);
			// this.cache.expire(detailCacheKey, Constant._1h);

			// dailySum 日累加
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					insertDailySumDimension(model, today);
					cdl.countDown();
				}
			});

			// acczset 排序集
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					insertAccZSetDimension(model, today);
					cdl.countDown();
				}
			});

			// dailyMultiDimensionZSet 多维度排序集，最初用于算搜索词，目前没什么用
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					insertDailyMultiDimensionZSet(model, today);
					cdl.countDown();
				}
			});

			// add map  单维度分别统计，最初是区分直销，渠道
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					insertMapDimension(model, primaryDimensionValue);
					cdl.countDown();
				}
			});

			// multiDimensionMap 秒级数据
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					insertMultiDimensionMap(model, primaryDimensionValue);
					cdl.countDown();
				}
			});

			// minuteMultiDimensionMap 分钟级数据
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					insertMinuteMultiDimensionMap(model, primaryDimensionValue);
					cdl.countDown();
				}
			});

			// hourlyMultiDimensionMap  小时级数据
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					insertHourlyMultiDimensionMap(model, primaryDimensionValue);
					cdl.countDown();
				}
			});

			// dailyMultiDimensionMap 天级数据
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					insertDailyMultiDimensionMap(model, primaryDimensionValue);
					cdl.countDown();
				}
			});

			cdl.await();
			return model;
		} catch (Exception e) {
			throw new PermissionDeniedDataAccessException("", e);
		}
	}

	private void insertDailySumDimension(T model, long today) {
		try {
			for (DimensionInfo dimensionInfo : dailySumDimensionInfos.values()) {
				FieldInfo dimension = dimensionInfo.getDimension();
				long dimensionValue = NumberUtils.toLong(dimension.getReadMethod().invoke(model).toString());
				String dailySumDimensionCacheKey = String.format(dimensionInfo.getCacheKeyPattern(), today);
				if (dimensionValue > 0) {
					cache.incrBy(dailySumDimensionCacheKey, dimensionValue);
					cache.expire(dailySumDimensionCacheKey, DaoConst.redis_1d);
				}
			}
		} catch (Exception e) {
			log.error("", e);
		}
	}

	private void insertAccZSetDimension(T model, long today) {
		try {
			for (DimensionInfo dimensionInfo : accZSetDimensionInfos.values()) {
				FieldInfo dimension = dimensionInfo.getDimension();
				String dimensionValue = dimension.getReadMethod().invoke(model).toString();
				long cacheValue = NumberUtils.toLong(dimensionInfo.getCacheValue().getReadMethod().invoke(model).toString());
				String accZSetDimensionCacheKey = String.format(dimensionInfo.getCacheKeyPattern(), today);
				if (!"null".equals(dimensionValue)) {
					cache.zincr(accZSetDimensionCacheKey, cacheValue, dimensionValue);
					cache.expire(accZSetDimensionCacheKey, DaoConst.redis_1d);
				}
			}
		} catch (Exception e) {
			log.error("", e);
		}
	}

	private void insertDailyMultiDimensionZSet(T model, long today) {
		try {
			for (DimensionInfo dimensionInfo : dailyMultiDimensionZSetInfos.values()) {
				FieldInfo dimension = dimensionInfo.getDimension();
				String dimensionValue = dimension.getReadMethod().invoke(model).toString();
				long cacheValue = NumberUtils.toLong(dimensionInfo.getCacheValue().getReadMethod().invoke(model).toString());
				String cacheKeyPattern = String.format(dimensionInfo.getCacheKeyPattern(), today);

				for (Set<FieldInfo> group : dimensionInfo.getCombination()) {
					String cacheKey = cacheKeyPattern;
					for (FieldInfo item : group) {
						cacheKey = cacheKey.replace(":" + item.getName(), item.getReadMethod().invoke(model).toString());
					}
					cache.zincr(cacheKey, cacheValue, dimensionValue);
					cache.expire(cacheKey, DaoConst.redis_1d);
				}
			}
		} catch (Exception e) {
			log.error("", e);
		}
	}

	private void insertMapDimension(T model, String primaryDimensionValue) {
		try {
			for (DimensionInfo dimensionInfo : mapDimensionInfos.values()) {
				FieldInfo dimension = dimensionInfo.getDimension();
				String dimensionValue = dimension.getReadMethod().invoke(model).toString();
				String cacheKey = dimensionInfo.getCacheKey().getReadMethod().invoke(model).toString();
				String cacheValue = dimensionInfo.getCacheValue().getReadMethod().invoke(model).toString();
				String mapDimensionCacheKey = String.format(dimensionInfo.getCacheKeyPattern(), primaryDimensionValue, dimensionValue);
				cache.hSetString(mapDimensionCacheKey, cacheKey, cacheValue);
				cache.expire(mapDimensionCacheKey, DaoConst.redis_10m);
			}
		} catch (Exception e) {
			log.error("", e);
		}
	}

	private void insertMultiDimensionMap(T model, String primaryDimensionValue) {
		try {
			for (DimensionInfo dimensionInfo : multiDimensionMapInfos.values()) {
				FieldInfo dimension = dimensionInfo.getDimension();
				long dimensionValue = NumberUtils.toLong(dimension.getReadMethod().invoke(model).toString());
				String primaryDimensionKey = String.format(dimensionInfo.getPrimaryDimensionKey(), primaryDimensionValue);
				String multiDimensionKeyPattern = dimensionInfo.getMultiDimensionKey();
				for (Set<FieldInfo> group : dimensionInfo.getCombination()) {
					String multiDimensionKey = multiDimensionKeyPattern;
					for (FieldInfo item : group) {
						multiDimensionKey = multiDimensionKey.replace(":" + item.getName(), item.getReadMethod().invoke(model).toString());
					}
					cache.hIncrBy(primaryDimensionKey, multiDimensionKey, dimensionValue);
				}
				cache.expire(primaryDimensionKey, DaoConst.redis_2h);
			}
		} catch (Exception e) {
			log.error("", e);
		}
	}

	protected void insertMinuteMultiDimensionMap(T model, String primaryDimensionValue) throws DataAccessException {
		try {
			for (DimensionInfo dimensionInfo : minuteMultiDimensionMapInfos.values()) {
				FieldInfo dimension = dimensionInfo.getDimension();
				long dimensionValue = NumberUtils.toLong(dimension.getReadMethod().invoke(model).toString());
				Long minute = DateUtils.truncateToMinute(NumberUtils.toLong(primaryDimensionValue));
				String primaryDimensionKey = String.format(dimensionInfo.getPrimaryDimensionKey(), minute);
				String multiDimensionKeyPattern = dimensionInfo.getMultiDimensionKey();
				for (Set<FieldInfo> group : dimensionInfo.getCombination()) {
					String multiDimensionKey = multiDimensionKeyPattern;
					for (FieldInfo item : group) {
						multiDimensionKey = multiDimensionKey.replace(":" + item.getName(), item.getReadMethod().invoke(model).toString());
					}
					cache.hIncrBy(primaryDimensionKey, multiDimensionKey, dimensionValue);
				}
				cache.expire(primaryDimensionKey, DaoConst.redis_2d);
			}
		} catch (Exception e) {
			log.error("", e);
		}
	}

	protected void insertHourlyMultiDimensionMap(T model, String primaryDimensionValue) throws DataAccessException {
		try {
			for (DimensionInfo dimensionInfo : hourlyMultiDimensionMapInfos.values()) {
				FieldInfo dimension = dimensionInfo.getDimension();
				long dimensionValue = NumberUtils.toLong(dimension.getReadMethod().invoke(model).toString());
				Long hour = DateUtils.truncateToHour(NumberUtils.toLong(primaryDimensionValue));
				String primaryDimensionKey = String.format(dimensionInfo.getPrimaryDimensionKey(), hour);
				String multiDimensionKeyPattern = dimensionInfo.getMultiDimensionKey();
				for (Set<FieldInfo> group : dimensionInfo.getCombination()) {
					String multiDimensionKey = multiDimensionKeyPattern;
					for (FieldInfo item : group) {
						multiDimensionKey = multiDimensionKey.replace(":" + item.getName(), item.getReadMethod().invoke(model).toString());
					}
					cache.hIncrBy(primaryDimensionKey, multiDimensionKey, dimensionValue);
				}
				cache.expire(primaryDimensionKey, DaoConst.redis_2d);
			}
		} catch (Exception e) {
			log.error("", e);
		}
	}

	protected void insertDailyMultiDimensionMap(T model, String primaryDimensionValue) {
		try {
			for (DimensionInfo dimensionInfo : dailyMultiDimensionMapInfos.values()) {
				FieldInfo dimension = dimensionInfo.getDimension();
				long dimensionValue = NumberUtils.toLong(dimension.getReadMethod().invoke(model).toString());
				Long day = DateUtils.truncateToToday(NumberUtils.toLong(primaryDimensionValue));
				String primaryDimensionKey = String.format(dimensionInfo.getPrimaryDimensionKey(), day);
				String multiDimensionKeyPattern = dimensionInfo.getMultiDimensionKey();
				for (Set<FieldInfo> group : dimensionInfo.getCombination()) {
					String multiDimensionKey = multiDimensionKeyPattern;
					for (FieldInfo item : group) {
						multiDimensionKey = multiDimensionKey.replace(":" + item.getName(), item.getReadMethod().invoke(model).toString());
					}
					cache.hIncrBy(primaryDimensionKey, multiDimensionKey, dimensionValue);
				}
				cache.expire(primaryDimensionKey, DaoConst.redis_1d);
			}
		} catch (Exception e) {
			log.error("", e);
		}
	}

	@SuppressWarnings("unchecked")
	@Deprecated
	protected List<T> cacheLoadDetail(T model, int count) throws DataAccessException {
		try {

			String primaryDimensionValue = super.primaryDimension.getReadMethod().invoke(model).toString();
			String detailCacheKey = String.format(detailCacheKeyPattern, primaryDimensionValue);
			Set<Object> results = this.cache.sRandMemberObject(detailCacheKey, count);

			if (results != null)
				return Lists.newArrayList((Set<T>) results);
			else
				return Collections.emptyList();
		} catch (Exception e) {
			throw new PermissionDeniedDataAccessException("", e);
		}
	}

	protected Set<Tuple> cacheLoadsAccZset(String key, long start, long end) {
		Set<Tuple> result = null;
		DimensionInfo dimensionInfo = accZSetDimensionInfos.get(key);
		if (dimensionInfo != null) {
			String accZSetDimensionCacheKey = String.format(dimensionInfo.getCacheKeyPattern(), DateUtils.getTruncatedToday());
			result = this.cache.zrevrangeWithScores(accZSetDimensionCacheKey, start, end);
		}
		if (result != null)
			return result;
		else
			return Collections.emptySet();
	}

	protected Set<Tuple> cacheLoadsDailyZset(String key, T model, long start, long end) {
		try {
			Set<Tuple> result = null;
			DimensionInfo dimensionInfo = dailyMultiDimensionZSetInfos.get(key);
			if (dimensionInfo != null) {
				String primaryDimensionValue = super.primaryDimension.getReadMethod().invoke(model).toString();
				String cacheKey = String.format(dimensionInfo.getCacheKeyPattern(), primaryDimensionValue);

				for (FieldInfo fieldInfo : dimensionInfo.getMultiDimension()) {
					Object value = fieldInfo.getReadMethod().invoke(model);
					if (value != null)
						cacheKey = cacheKey.replace(":" + fieldInfo.getName(), value.toString());
				}
				result = this.cache.zrevrangeWithScores(cacheKey, start, end);
			}
			if (result != null)
				return result;
			else
				return Collections.emptySet();
		} catch (Exception e) {
			throw new PermissionDeniedDataAccessException("", e);
		}
	}

	protected Map<String, String> cacheLoadMapDimension(String key, T model) {
		try {
			Map<String, String> result = null;
			DimensionInfo dimensionInfo = mapDimensionInfos.get(key);
			if (dimensionInfo != null) {
				String primaryDimensionValue = super.primaryDimension.getReadMethod().invoke(model).toString();
				FieldInfo dimension = dimensionInfo.getDimension();
				String dimensionValue = dimension.getReadMethod().invoke(model).toString();
				String mapDimensionCacheKey = String.format(dimensionInfo.getCacheKeyPattern(), primaryDimensionValue, dimensionValue);
				result = this.cache.hGetAllString(mapDimensionCacheKey);
			}
			if (result != null)
				return result;
			else
				return Maps.newHashMap();

		} catch (Exception e) {
			throw new PermissionDeniedDataAccessException("", e);
		}
	}

	protected long cacheLoadSumDimension(String key) {
		DimensionInfo dimensionInfo = dailySumDimensionInfos.get(key);
		String result = null;
		if (dimensionInfo != null) {
			String dailySumDimensionCacheKey = String.format(dimensionInfo.getCacheKeyPattern(), DateUtils.getTruncatedToday());
			result = this.cache.getString(dailySumDimensionCacheKey);
		}
		return NumberUtils.toLong(result);
	}

	protected long cacheLoadMultiDimensionValue(String dimensionName, T model) {
		DimensionInfo dimensionInfo = multiDimensionMapInfos.get(dimensionName);
		return cacheloadMultiDimensionValue(dimensionInfo, model);
	}

	protected long cacheLoadMinuteMultiDimensionValue(String dimensionName, T model) {
		DimensionInfo dimensionInfo = minuteMultiDimensionMapInfos.get(dimensionName);
		return cacheloadMultiDimensionValue(dimensionInfo, model);
	}

	protected long cacheLoadHourlyMultiDimensionValue(String dimensionName, T model) {
		DimensionInfo dimensionInfo = hourlyMultiDimensionMapInfos.get(dimensionName);
		return cacheloadMultiDimensionValue(dimensionInfo, model);
	}

	protected long cacheLoadDailyMultiDimensionValue(String dimensionName, T model) {
		DimensionInfo dimensionInfo = dailyMultiDimensionMapInfos.get(dimensionName);
		return cacheloadMultiDimensionValue(dimensionInfo, model);
	}

	private long cacheloadMultiDimensionValue(DimensionInfo dimensionInfo, T model) {
		try {
			String result = null;
			if (dimensionInfo != null) {
				String primaryDimensionValue = super.primaryDimension.getReadMethod().invoke(model).toString();
				String primaryDimensionKey = String.format(dimensionInfo.getPrimaryDimensionKey(), primaryDimensionValue);
				String multiDimensionKey = dimensionInfo.getMultiDimensionKey();

				for (FieldInfo fieldInfo : super.fieldInfos.values()) {
					Object value = fieldInfo.getReadMethod().invoke(model);
					if (value != null)
						multiDimensionKey = multiDimensionKey.replace(":" + fieldInfo.getName(), value.toString());
				}
				result = this.cache.hGetString(primaryDimensionKey, multiDimensionKey);
			}
			return NumberUtils.toLong(result);
		} catch (Exception e) {
			log.error("",e);
			throw new PermissionDeniedDataAccessException("", e);
		}
	}

	/**
	 * 获取最新时间戳
	 * @param model
	 */
	public String getLatestPrimaryDimension() {
		return this.cache.getString(latestCacheKey);
	}

	/**
	 * 设置最新时间戳
	 * @param model
	 */
	protected void setLatestPrimaryDimension(T model) {
		try {
			String primaryDimensionValue = super.primaryDimension.getReadMethod().invoke(model).toString();
			this.cache.setString(latestCacheKey, 0, primaryDimensionValue);
		} catch (Exception e) {
			throw new PermissionDeniedDataAccessException("", e);
		}
	}


	public void resetMinuteMultiDimension(T model,String fieldName) throws DataAccessException {
		try {
				DimensionInfo dimensionInfo = minuteMultiDimensionMapInfos.get(fieldName);
				FieldInfo dimension = dimensionInfo.getDimension();
				Object dimensionVal = dimension.getReadMethod().invoke(model);
				long dimensionValue = NumberUtils.toLong(dimensionVal.toString());
				long minute = NumberUtils.toLong(primaryDimension.getReadMethod().invoke(model).toString());
				String primaryDimensionKey = String.format(dimensionInfo.getPrimaryDimensionKey(), minute);
				String multiDimensionKeyPattern = dimensionInfo.getMultiDimensionKey();
				for (Set<FieldInfo> group : dimensionInfo.getCombination()) {
					String multiDimensionKey = multiDimensionKeyPattern;
					for (FieldInfo item : group) {
						multiDimensionKey = multiDimensionKey.replace(":" + item.getName(), item.getReadMethod().invoke(model).toString());
					}
					cache.hSetString(primaryDimensionKey, multiDimensionKey, String.valueOf(dimensionValue));
				}
				cache.expire(primaryDimensionKey, DaoConst.redis_2d);
		} catch (Exception e) {
			log.error("", e);
		}
	}
	
}
